Shiny App Experiment Lab

Libraries

library(here)
library(dplyr)
library(arrow)
library(tidyr)
library(geojsonio)

Reading data

census_dataset <- open_dataset(here("data", "processed", "parquet_data_coords"))
census_dataset
FileSystemDataset with 33 Parquet files
NOM_MUN: string
NOM_LOC: string
LONGITUD: string
LATITUD: string
POBTOT: double
REL_H_M: string
POB0_14: string
POB15_64: string
POB65_MAS: string
P_0A4: string
P_0A4_F: string
P_0A4_M: string
P_5A9: string
P_5A9_F: string
P_5A9_M: string
P_10A14: string
P_10A14_F: string
P_10A14_M: string
P_15A19: string
P_15A19_F: string
P_15A19_M: string
P_20A24: string
P_20A24_F: string
P_20A24_M: string
P_25A29: string
P_25A29_F: string
P_25A29_M: string
P_30A34: string
P_30A34_F: string
P_30A34_M: string
P_35A39: string
P_35A39_F: string
P_35A39_M: string
P_40A44: string
P_40A44_F: string
P_40A44_M: string
P_45A49: string
P_45A49_F: string
P_45A49_M: string
P_50A54: string
P_50A54_F: string
P_50A54_M: string
P_55A59: string
P_55A59_F: string
P_55A59_M: string
P_60A64: string
P_60A64_F: string
P_60A64_M: string
P_65A69: string
P_65A69_F: string
P_65A69_M: string
P_70A74: string
P_70A74_F: string
P_70A74_M: string
P_75A79: string
P_75A79_F: string
P_75A79_M: string
P_80A84: string
P_80A84_F: string
P_80A84_M: string
P_85YMAS: string
P_85YMAS_F: string
P_85YMAS_M: string
PROM_HNV: string
PNACENT: string
PNACENT_F: string
PNACENT_M: string
PNACOE: string
PNACOE_F: string
PNACOE_M: string
longitude_decimal: double
latitude_decimal: double
NOM_ENT: string

See $metadata for additional Schema metadata

Reading specific data

pueb_norm <- census_dataset |>
    filter(NOM_ENT=="Puebla") |> 
    collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
pueb_norm
unique(pueb_norm$NOM_MUN)
  [1] "Total de la entidad Puebla"       "Acajete"                          "Acateno"                          "Acatlán"                         
  [5] "Acatzingo"                        "Acteopan"                         "Ahuacatlán"                       "Ahuatlán"                        
  [9] "Ahuazotepec"                      "Ahuehuetitla"                     "Ajalpan"                          "Albino Zertuche"                 
 [13] "Aljojuca"                         "Altepexi"                         "Amixtlán"                         "Amozoc"                          
 [17] "Aquixtla"                         "Atempan"                          "Atexcal"                          "Atlixco"                         
 [21] "Atoyatempan"                      "Atzala"                           "Atzitzihuacán"                    "Atzitzintla"                     
 [25] "Axutla"                           "Ayotoxco de Guerrero"             "Calpan"                           "Caltepec"                        
 [29] "Camocuautla"                      "Caxhuacan"                        "Coatepec"                         "Coatzingo"                       
 [33] "Cohetzala"                        "Cohuecan"                         "Coronango"                        "Coxcatlán"                       
 [37] "Coyomeapan"                       "Coyotepec"                        "Cuapiaxtla de Madero"             "Cuautempan"                      
 [41] "Cuautinchán"                      "Cuautlancingo"                    "Cuayuca de Andrade"               "Cuetzalan del Progreso"          
 [45] "Cuyoaco"                          "Chalchicomula de Sesma"           "Chapulco"                         "Chiautla"                        
 [49] "Chiautzingo"                      "Chiconcuautla"                    "Chichiquila"                      "Chietla"                         
 [53] "Chigmecatitlán"                   "Chignahuapan"                     "Chignautla"                       "Chila"                           
 [57] "Chila de la Sal"                  "Honey"                            "Chilchotla"                       "Chinantla"                       
 [61] "Domingo Arenas"                   "Eloxochitlán"                     "Epatlán"                          "Esperanza"                       
 [65] "Francisco Z. Mena"                "General Felipe Ángeles"           "Guadalupe"                        "Guadalupe Victoria"              
 [69] "Hermenegildo Galeana"             "Huaquechula"                      "Huatlatlauca"                     "Huauchinango"                    
 [73] "Huehuetla"                        "Huehuetlán el Chico"              "Huejotzingo"                      "Hueyapan"                        
 [77] "Hueytamalco"                      "Hueytlalpan"                      "Huitzilan de Serdán"              "Huitziltepec"                    
 [81] "Atlequizayan"                     "Ixcamilpa de Guerrero"            "Ixcaquixtla"                      "Ixtacamaxtitlán"                 
 [85] "Ixtepec"                          "Izúcar de Matamoros"              "Jalpan"                           "Jolalpan"                        
 [89] "Jonotla"                          "Jopala"                           "Juan C. Bonilla"                  "Juan Galindo"                    
 [93] "Juan N. Méndez"                   "Lafragua"                         "Libres"                           "La Magdalena Tlatlauquitepec"    
 [97] "Mazapiltepec de Juárez"           "Mixtla"                           "Molcaxac"                         "Cañada Morelos"                  
[101] "Naupan"                           "Nauzontla"                        "Nealtican"                        "Nicolás Bravo"                   
[105] "Nopalucan"                        "Ocotepec"                         "Ocoyucan"                         "Olintla"                         
[109] "Oriental"                         "Pahuatlán"                        "Palmar de Bravo"                  "Pantepec"                        
[113] "Petlalcingo"                      "Piaxtla"                          "Puebla"                           "Quecholac"                       
[117] "Quimixtlán"                       "Rafael Lara Grajales"             "Los Reyes de Juárez"              "San Andrés Cholula"              
[121] "San Antonio Cañada"               "San Diego la Mesa Tochimiltzingo" "San Felipe Teotlalcingo"          "San Felipe Tepatlán"             
[125] "San Gabriel Chilac"               "San Gregorio Atzompa"             "San Jerónimo Tecuanipan"          "San Jerónimo Xayacatlán"         
[129] "San José Chiapa"                  "San José Miahuatlán"              "San Juan Atenco"                  "San Juan Atzompa"                
[133] "San Martín Texmelucan"            "San Martín Totoltepec"            "San Matías Tlalancaleca"          "San Miguel Ixitlán"              
[137] "San Miguel Xoxtla"                "San Nicolás Buenos Aires"         "San Nicolás de los Ranchos"       "San Pablo Anicano"               
[141] "San Pedro Cholula"                "San Pedro Yeloixtlahuaca"         "San Salvador el Seco"             "San Salvador el Verde"           
[145] "San Salvador Huixcolotla"         "San Sebastián Tlacotepec"         "Santa Catarina Tlaltempan"        "Santa Inés Ahuatempan"           
[149] "Santa Isabel Cholula"             "Santiago Miahuatlán"              "Huehuetlán el Grande"             "Santo Tomás Hueyotlipan"         
[153] "Soltepec"                         "Tecali de Herrera"                "Tecamachalco"                     "Tecomatlán"                      
[157] "Tehuacán"                         "Tehuitzingo"                      "Tenampulco"                       "Teopantlán"                      
[161] "Teotlalco"                        "Tepanco de López"                 "Tepango de Rodríguez"             "Tepatlaxco de Hidalgo"           
[165] "Tepeaca"                          "Tepemaxalco"                      "Tepeojuma"                        "Tepetzintla"                     
[169] "Tepexco"                          "Tepexi de Rodríguez"              "Tepeyahualco"                     "Tepeyahualco de Cuauhtémoc"      
[173] "Tetela de Ocampo"                 "Teteles de Avila Castillo"        "Teziutlán"                        "Tianguismanalco"                 
[177] "Tilapa"                           "Tlacotepec de Benito Juárez"      "Tlacuilotepec"                    "Tlachichuca"                     
[181] "Tlahuapan"                        "Tlaltenango"                      "Tlanepantla"                      "Tlaola"                          
[185] "Tlapacoya"                        "Tlapanalá"                        "Tlatlauquitepec"                  "Tlaxco"                          
[189] "Tochimilco"                       "Tochtepec"                        "Totoltepec de Guerrero"           "Tulcingo"                        
[193] "Tuzamapan de Galeana"             "Tzicatlacoyan"                    "Venustiano Carranza"              "Vicente Guerrero"                
[197] "Xayacatlán de Bravo"              "Xicotepec"                        "Xicotlán"                         "Xiutetelco"                      
[201] "Xochiapulco"                      "Xochiltepec"                      "Xochitlán de Vicente Suárez"      "Xochitlán Todos Santos"          
[205] "Yaonáhuac"                        "Yehualtepec"                      "Zacapala"                         "Zacapoaxtla"                     
[209] "Zacatlán"                         "Zapotitlán"                       "Zapotitlán de Méndez"             "Zaragoza"                        
[213] "Zautla"                           "Zihuateutla"                      "Zinacatepec"                      "Zongozotla"                      
[217] "Zoquiapan"                        "Zoquitlán"                       

Example

extract_coordinates <- function(data, municipality, locality) {
  selected_location <- data |> 
    filter(NOM_MUN == municipality, NOM_LOC == locality)
  
  coordinates <- tibble(
    long = selected_location$longitude_decimal,
    lat = selected_location$latitude_decimal
  )
  
  return(coordinates)
}


municipality <- "Acajete"
locality <- "Santa Isabel Tepetzala"

red_point <- extract_coordinates(pueb_norm, municipality, locality) |> 
  slice(1)

# red_point <- data.frame(long = -98.2035, lat = 19.0414)
geojson_file <- geojson_read("../data/processed/mexico.geojson",  what = "sp")

filtered_geojson <- geojson_file |> 
  filter(name == "Puebla")

ggplot() +
  geom_polygon(data = filtered_geojson,
               aes(x = long, y = lat, group = group),
               fill = "lightgray", color = "white") +
  geom_point(data = red_point, aes(x = long, y = lat), color = "red", size = 3) +
  theme_void() +
  coord_map()
Warning: `fortify(<SpatialPolygonsDataFrame>)` was deprecated in ggplot2 3.4.4.
Please migrate to sf.Regions defined for each Polygons

Population graph

pueb_norm

Getting column names

column_names <- names(pueb_norm)
column_names
 [1] "NOM_MUN"           "NOM_LOC"           "LONGITUD"          "LATITUD"           "POBTOT"            "REL_H_M"           "POB0_14"          
 [8] "POB15_64"          "POB65_MAS"         "P_0A4"             "P_0A4_F"           "P_0A4_M"           "P_5A9"             "P_5A9_F"          
[15] "P_5A9_M"           "P_10A14"           "P_10A14_F"         "P_10A14_M"         "P_15A19"           "P_15A19_F"         "P_15A19_M"        
[22] "P_20A24"           "P_20A24_F"         "P_20A24_M"         "P_25A29"           "P_25A29_F"         "P_25A29_M"         "P_30A34"          
[29] "P_30A34_F"         "P_30A34_M"         "P_35A39"           "P_35A39_F"         "P_35A39_M"         "P_40A44"           "P_40A44_F"        
[36] "P_40A44_M"         "P_45A49"           "P_45A49_F"         "P_45A49_M"         "P_50A54"           "P_50A54_F"         "P_50A54_M"        
[43] "P_55A59"           "P_55A59_F"         "P_55A59_M"         "P_60A64"           "P_60A64_F"         "P_60A64_M"         "P_65A69"          
[50] "P_65A69_F"         "P_65A69_M"         "P_70A74"           "P_70A74_F"         "P_70A74_M"         "P_75A79"           "P_75A79_F"        
[57] "P_75A79_M"         "P_80A84"           "P_80A84_F"         "P_80A84_M"         "P_85YMAS"          "P_85YMAS_F"        "P_85YMAS_M"       
[64] "PROM_HNV"          "PNACENT"           "PNACENT_F"         "PNACENT_M"         "PNACOE"            "PNACOE_F"          "PNACOE_M"         
[71] "longitude_decimal" "latitude_decimal"  "NOM_ENT"          

Getting column names that have to do with population age cohorts (Masculine/Feminine)

matching_columns <- grep("^P_.*[MF]$", column_names, value = TRUE)
matching_columns
 [1] "P_0A4_F"    "P_0A4_M"    "P_5A9_F"    "P_5A9_M"    "P_10A14_F"  "P_10A14_M"  "P_15A19_F"  "P_15A19_M"  "P_20A24_F"  "P_20A24_M"  "P_25A29_F" 
[12] "P_25A29_M"  "P_30A34_F"  "P_30A34_M"  "P_35A39_F"  "P_35A39_M"  "P_40A44_F"  "P_40A44_M"  "P_45A49_F"  "P_45A49_M"  "P_50A54_F"  "P_50A54_M" 
[23] "P_55A59_F"  "P_55A59_M"  "P_60A64_F"  "P_60A64_M"  "P_65A69_F"  "P_65A69_M"  "P_70A74_F"  "P_70A74_M"  "P_75A79_F"  "P_75A79_M"  "P_80A84_F" 
[34] "P_80A84_M"  "P_85YMAS_F" "P_85YMAS_M"

Separate by sex

ending_in_M <- character(0)
ending_in_F <- character(0)

for (col_name in matching_columns) {
  if (endsWith(col_name, "M")) {
    ending_in_M <- c(ending_in_M, col_name)
  } else if (endsWith(col_name, "F")) {
    ending_in_F <- c(ending_in_F, col_name)
  }
}

print("Column names ending in M:")
[1] "Column names ending in M:"
print(ending_in_M)
 [1] "P_0A4_M"    "P_5A9_M"    "P_10A14_M"  "P_15A19_M"  "P_20A24_M"  "P_25A29_M"  "P_30A34_M"  "P_35A39_M"  "P_40A44_M"  "P_45A49_M"  "P_50A54_M" 
[12] "P_55A59_M"  "P_60A64_M"  "P_65A69_M"  "P_70A74_M"  "P_75A79_M"  "P_80A84_M"  "P_85YMAS_M"
print("Column names ending in F:")
[1] "Column names ending in F:"
print(ending_in_F)
 [1] "P_0A4_F"    "P_5A9_F"    "P_10A14_F"  "P_15A19_F"  "P_20A24_F"  "P_25A29_F"  "P_30A34_F"  "P_35A39_F"  "P_40A44_F"  "P_45A49_F"  "P_50A54_F" 
[12] "P_55A59_F"  "P_60A64_F"  "P_65A69_F"  "P_70A74_F"  "P_75A79_F"  "P_80A84_F"  "P_85YMAS_F"
cohort_names_m <- c("P_0A4_M",
                    "P_5A9_M",
                    "P_10A14_M",
                    "P_15A19_M",
                    "P_20A24_M",
                    "P_25A29_M",
                    "P_30A34_M",
                    "P_35A39_M",
                    "P_40A44_M",
                    "P_45A49_M",
                    "P_50A54_M",
                    "P_55A59_M",
                    "P_60A64_M",
                    "P_65A69_M",
                    "P_70A74_M",
                    "P_75A79_M",
                    "P_80A84_M",
                    "P_85YMAS_M")

cohort_names_f <- c("P_0A4_F",
                    "P_5A9_F",
                    "P_10A14_F",
                    "P_15A19_F",
                    "P_20A24_F",
                    "P_25A29_F",
                    "P_30A34_F",
                    "P_35A39_F",
                    "P_40A44_F",
                    "P_45A49_F",
                    "P_50A54_F",
                    "P_55A59_F",
                    "P_60A64_F",
                    "P_65A69_F", 
                    "P_70A74_F",
                    "P_75A79_F",
                    "P_80A84_F", 
                    "P_85YMAS_F")



municipality <- "Acajete"
locality <- "San Javier"

pueb_norm_filt <- pueb_norm |> 
    filter(NOM_MUN == municipality, NOM_LOC == locality)

cohort_counts_m <- as.numeric(pueb_norm_filt[1,cohort_names_m])
cohort_counts_f <- as.numeric(pueb_norm_filt[1,cohort_names_f])

data <- tibble(
  Cohort = c(cohort_names_m, cohort_names_f),
  Count = c(cohort_counts_m, cohort_counts_f),
  Sex = rep(c("Male", "Female"), each = length(cohort_names_m))
)

# Plotting population pyramid
ggplot(data, aes(x = reorder(Cohort, -Count), y = Count, fill = Sex)) +
  geom_bar(stat = "identity", position = "identity") +
  scale_fill_manual(values = c("blue", "pink")) +  
  coord_flip() +  
  labs(title = "Population Pyramid",
       x = "Population Count",
       y = "Age Cohort",
       fill = "Sex") +
  theme_minimal()  

new_ages <- c("0-4",
                 "5-9",
                 "10-14",
                 "15-19",
                 "20-24",
                 "25-29", 
                 "30-34", 
                 "35-39", 
                 "40-44", 
                 "45-49",
                 "50-54",
                 "55-59",
                 "60-64", 
                 "65-69",
                 "70-74", 
                 "75-79", 
                 "80-84", 
                 "85+")

data <- tibble(
  Age = paste0(new_ages),
  Male = sample(200:1000, length(cohort_names_m), replace = TRUE),
  Female = sample(200:1000, length(cohort_names_f), replace = TRUE)
)

data_long <- pivot_longer(
  data, 
  cols = c(Male, Female), 
  names_to = "Sex", 
  values_to = "Population"
)

basic_plot <- ggplot(data_long, aes(x = Age, y = ifelse(Sex == "Male", -Population, Population), fill = Sex)) +
  geom_bar(stat = "identity") +
  scale_y_continuous(labels = abs, limits = max(data_long$Population) * c(-1, 1)) +
  coord_flip() +
  theme_minimal() +
  labs(x = "Age", y = "Population", fill = "Sex", title = "Population Pyramid")

basic_plot

Complete pipeline

census_dataset <- open_dataset(here("data", "processed", "parquet_data_coords"))


pueb_norm <- census_dataset |>
    filter(NOM_ENT=="Puebla") |> 
    collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
municipality <- "Acajete"
locality <- "San Javier"

pueb_norm_filt <- pueb_norm |> 
    filter(NOM_MUN == municipality, NOM_LOC == locality)


cohort_names_m <- c("P_0A4_M",
                    "P_5A9_M",
                    "P_10A14_M",
                    "P_15A19_M",
                    "P_20A24_M",
                    "P_25A29_M",
                    "P_30A34_M",
                    "P_35A39_M",
                    "P_40A44_M",
                    "P_45A49_M",
                    "P_50A54_M",
                    "P_55A59_M",
                    "P_60A64_M",
                    "P_65A69_M",
                    "P_70A74_M",
                    "P_75A79_M",
                    "P_80A84_M",
                    "P_85YMAS_M")

cohort_names_f <- c("P_0A4_F",
                    "P_5A9_F",
                    "P_10A14_F",
                    "P_15A19_F",
                    "P_20A24_F",
                    "P_25A29_F",
                    "P_30A34_F",
                    "P_35A39_F",
                    "P_40A44_F",
                    "P_45A49_F",
                    "P_50A54_F",
                    "P_55A59_F",
                    "P_60A64_F",
                    "P_65A69_F", 
                    "P_70A74_F",
                    "P_75A79_F",
                    "P_80A84_F", 
                    "P_85YMAS_F")



new_ages <- c("0-4",
                 "5-9",
                 "10-14",
                 "15-19",
                 "20-24",
                 "25-29", 
                 "30-34", 
                 "35-39", 
                 "40-44", 
                 "45-49",
                 "50-54",
                 "55-59",
                 "60-64", 
                 "65-69",
                 "70-74", 
                 "75-79", 
                 "80-84", 
                 "85+")


data <- tibble(
  Age = paste0(new_ages),
  Male = as.numeric(pueb_norm_filt[1,cohort_names_m]),
  Female = as.numeric(pueb_norm_filt[1,cohort_names_f])
)

data_long <- pivot_longer(
  data, 
  cols = c(Male, Female), 
  names_to = "Sex", 
  values_to = "Population"
)
basic_plot <- ggplot(data_long, aes(x = Age, y = ifelse(Sex == "Male", -Population, Population), fill = Sex)) +
  geom_bar(stat = "identity") +
  scale_y_continuous(labels = abs, limits = max(data_long$Population) * c(-1, 1)) +
  coord_flip() +
  theme_minimal() +
  labs(x = "Age", y = "Population", fill = "Sex", title = "Population Pyramid")

basic_plot

Card

card <- census_dataset |>
    filter(
      NOM_ENT == "Puebla",
      NOM_MUN == "Acateno",
      NOM_LOC == "Santa Andrea"
    ) |>
  collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
    
    total_population <- as.numeric(card[1, c("POBTOT")], na.rm = TRUE)
    paste("Total Population:", total_population)
[1] "Total Population: 1"

Pie

tot <- census_dataset |>
    filter(NOM_ENT=="Total nacional") |> 
    collect()
Warning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$rWarning: Invalid metadata$r
tot
# origin <- census_dataset |>  
#      filter(
#       NOM_ENT == "Puebla",
#       NOM_MUN == "Acateno",
#       NOM_LOC == "Santa Andrea"
#     ) |>
#   collect()
    
tot
    
    # Extract birth data
    birth_local <- as.numeric(tot[1, "PNACENT"])
    birth_another <- as.numeric(tot[1, "PNACOE"])
    
    # Debugging output
    print(paste("Birth Local:", birth_local))
[1] "Birth Local: 102724322"
    print(paste("Birth Another:", birth_another))
[1] "Birth Another: 21611963"
    
    # Create ratio dataframe
    ratio_df <- tibble(
      Category = c("Local", "Other"),
      Ratio = c(birth_local, birth_another)
    )
    
    # Calculate percentages
    ratio_df$Percentage <- ratio_df$Ratio / sum(ratio_df$Ratio) * 100
    
    # Plot the pie chart
    gg <- ggplot(ratio_df, aes(x = "", y = Ratio, fill = Category)) +
      geom_bar(stat = "identity", width = 1) +
      coord_polar(theta = "y") +
      theme_void() +
      theme(legend.position = "bottom") +
      scale_fill_manual(values = c("#00BFC4", "#F8766D")) +
      geom_text(aes(label = paste0(round(Percentage), "%")), 
                position = position_stack(vjust = 0.5),
                size = 5, color = "white", fontface = "bold")
    
    gg

NA

Read CSV from URL

test_csv <- read.csv("https://raw.github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/master/data/processed/data_coords.csv?token=GHSAT0AAAAAAAAACLO5WK36WJ6S74E3NU4CZRJJJGA")

test_csv
NA
filtered_data <- filter(test_csv, NOM_ENT == "Puebla")
filtered_data

Download with curl

library(curl)

# Define the URL of the CSV file
url <- "https://raw.github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/master/data/processed/data_coords.csv?token=GHSAT0AAAAAAAAACLO5WK36WJ6S74E3NU4CZRJJJGA"

response <- curl::curl_fetch_memory(url)

if (response$status_code == 200) {
  csv_content <- rawToChar(response$content)
    test_csv <- read.csv(text = csv_content)
print(test_csv)
}
library(RCurl)
x <- getURL("https://raw.github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/master/data/processed/data_coords.csv?token=GHSAT0AAAAAAAAACLO5WK36WJ6S74E3NU4CZRJJJGA")
y <- read.csv(text = x)
y
test_2 <- arrow::read_parquet("https://github.ubc.ca/MDS-2023-24/DSCI_532_individual-assignment_marcony1/tree/master/data/processed/parquet_data_coords.parquet")
Error: Invalid: Parquet magic bytes not found in footer. Either the file is corrupted or this is not a parquet file.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogTWFyY28gUG9sbyBCcmF2byBNb250aWVsDQpkYXRlOiAyMDIwLTA0LTIzDQotLS0NCg0KIyBTaGlueSBBcHAgRXhwZXJpbWVudCBMYWINCg0KIyMjIExpYnJhcmllcw0KDQpgYGB7cn0NCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGFycm93KQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2VvanNvbmlvKQ0KYGBgDQoNCiMjIyBSZWFkaW5nIGRhdGENCg0KYGBge3J9DQpjZW5zdXNfZGF0YXNldCA8LSBvcGVuX2RhdGFzZXQoaGVyZSgiZGF0YSIsICJwcm9jZXNzZWQiLCAicGFycXVldF9kYXRhX2Nvb3JkcyIpKQ0KY2Vuc3VzX2RhdGFzZXQNCg0KYGBgDQoNCiMjIyBSZWFkaW5nIHNwZWNpZmljIGRhdGENCg0KYGBge3J9DQpwdWViX25vcm0gPC0gY2Vuc3VzX2RhdGFzZXQgfD4NCiAgICBmaWx0ZXIoTk9NX0VOVD09IlB1ZWJsYSIpIHw+IA0KICAgIGNvbGxlY3QoKQ0KDQpwdWViX25vcm0NCmBgYA0KDQpgYGB7cn0NCnVuaXF1ZShwdWViX25vcm0kTk9NX01VTikNCmBgYA0KDQojIyMgRXhhbXBsZQ0KDQpgYGB7cn0NCmV4dHJhY3RfY29vcmRpbmF0ZXMgPC0gZnVuY3Rpb24oZGF0YSwgbXVuaWNpcGFsaXR5LCBsb2NhbGl0eSkgew0KICBzZWxlY3RlZF9sb2NhdGlvbiA8LSBkYXRhIHw+IA0KICAgIGZpbHRlcihOT01fTVVOID09IG11bmljaXBhbGl0eSwgTk9NX0xPQyA9PSBsb2NhbGl0eSkNCiAgDQogIGNvb3JkaW5hdGVzIDwtIHRpYmJsZSgNCiAgICBsb25nID0gc2VsZWN0ZWRfbG9jYXRpb24kbG9uZ2l0dWRlX2RlY2ltYWwsDQogICAgbGF0ID0gc2VsZWN0ZWRfbG9jYXRpb24kbGF0aXR1ZGVfZGVjaW1hbA0KICApDQogIA0KICByZXR1cm4oY29vcmRpbmF0ZXMpDQp9DQoNCg0KbXVuaWNpcGFsaXR5IDwtICJBY2FqZXRlIg0KbG9jYWxpdHkgPC0gIlNhbnRhIElzYWJlbCBUZXBldHphbGEiDQoNCnJlZF9wb2ludCA8LSBleHRyYWN0X2Nvb3JkaW5hdGVzKHB1ZWJfbm9ybSwgbXVuaWNpcGFsaXR5LCBsb2NhbGl0eSkgfD4gDQogIHNsaWNlKDEpDQoNCiMgcmVkX3BvaW50IDwtIGRhdGEuZnJhbWUobG9uZyA9IC05OC4yMDM1LCBsYXQgPSAxOS4wNDE0KQ0KZ2VvanNvbl9maWxlIDwtIGdlb2pzb25fcmVhZCgiLi4vZGF0YS9wcm9jZXNzZWQvbWV4aWNvLmdlb2pzb24iLCAgd2hhdCA9ICJzcCIpDQoNCmZpbHRlcmVkX2dlb2pzb24gPC0gZ2VvanNvbl9maWxlIHw+IA0KICBmaWx0ZXIobmFtZSA9PSAiUHVlYmxhIikNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3BvbHlnb24oZGF0YSA9IGZpbHRlcmVkX2dlb2pzb24sDQogICAgICAgICAgICAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLA0KICAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGdyYXkiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gcmVkX3BvaW50LCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykgKw0KICB0aGVtZV92b2lkKCkgKw0KICBjb29yZF9tYXAoKQ0KYGBgDQoNCiMjIyBQb3B1bGF0aW9uIGdyYXBoDQoNCmBgYHtyfQ0KcHVlYl9ub3JtDQpgYGANCg0KIyMjIEdldHRpbmcgY29sdW1uIG5hbWVzDQoNCmBgYHtyfQ0KY29sdW1uX25hbWVzIDwtIG5hbWVzKHB1ZWJfbm9ybSkNCmNvbHVtbl9uYW1lcw0KYGBgDQoNCiMjIyBHZXR0aW5nIGNvbHVtbiBuYW1lcyB0aGF0IGhhdmUgdG8gZG8gd2l0aCBwb3B1bGF0aW9uIGFnZSBjb2hvcnRzIChNYXNjdWxpbmUvRmVtaW5pbmUpDQoNCmBgYHtyfQ0KbWF0Y2hpbmdfY29sdW1ucyA8LSBncmVwKCJeUF8uKltNRl0kIiwgY29sdW1uX25hbWVzLCB2YWx1ZSA9IFRSVUUpDQptYXRjaGluZ19jb2x1bW5zDQpgYGANCg0KIyMjIFNlcGFyYXRlIGJ5IHNleA0KDQpgYGB7cn0NCmVuZGluZ19pbl9NIDwtIGNoYXJhY3RlcigwKQ0KZW5kaW5nX2luX0YgPC0gY2hhcmFjdGVyKDApDQoNCmZvciAoY29sX25hbWUgaW4gbWF0Y2hpbmdfY29sdW1ucykgew0KICBpZiAoZW5kc1dpdGgoY29sX25hbWUsICJNIikpIHsNCiAgICBlbmRpbmdfaW5fTSA8LSBjKGVuZGluZ19pbl9NLCBjb2xfbmFtZSkNCiAgfSBlbHNlIGlmIChlbmRzV2l0aChjb2xfbmFtZSwgIkYiKSkgew0KICAgIGVuZGluZ19pbl9GIDwtIGMoZW5kaW5nX2luX0YsIGNvbF9uYW1lKQ0KICB9DQp9DQoNCnByaW50KCJDb2x1bW4gbmFtZXMgZW5kaW5nIGluIE06IikNCnByaW50KGVuZGluZ19pbl9NKQ0KDQpwcmludCgiQ29sdW1uIG5hbWVzIGVuZGluZyBpbiBGOiIpDQpwcmludChlbmRpbmdfaW5fRikNCmBgYA0KDQpgYGB7cn0NCmNvaG9ydF9uYW1lc19tIDwtIGMoIlBfMEE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF81QTlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzEwQTE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8xNUExOV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjBBMjRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzI1QTI5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8zMEEzNF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMzVBMzlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzQwQTQ0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF80NUE0OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTBBNTRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzU1QTU5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF82MEE2NF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNjVBNjlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzg1WU1BU19NIikNCg0KY29ob3J0X25hbWVzX2YgPC0gYygiUF8wQTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzVBOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMTBBMTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzE1QTE5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8yMEEyNF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjVBMjlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzMwQTM0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8zNUEzOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNDBBNDRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzQ1QTQ5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF81MEE1NF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTVBNTlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzYwQTY0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF82NUE2OV9GIiwgDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfRiIsIA0KICAgICAgICAgICAgICAgICAgICAiUF84NVlNQVNfRiIpDQoNCg0KDQptdW5pY2lwYWxpdHkgPC0gIkFjYWpldGUiDQpsb2NhbGl0eSA8LSAiU2FuIEphdmllciINCg0KcHVlYl9ub3JtX2ZpbHQgPC0gcHVlYl9ub3JtIHw+IA0KICAgIGZpbHRlcihOT01fTVVOID09IG11bmljaXBhbGl0eSwgTk9NX0xPQyA9PSBsb2NhbGl0eSkNCg0KY29ob3J0X2NvdW50c19tIDwtIGFzLm51bWVyaWMocHVlYl9ub3JtX2ZpbHRbMSxjb2hvcnRfbmFtZXNfbV0pDQpjb2hvcnRfY291bnRzX2YgPC0gYXMubnVtZXJpYyhwdWViX25vcm1fZmlsdFsxLGNvaG9ydF9uYW1lc19mXSkNCg0KZGF0YSA8LSB0aWJibGUoDQogIENvaG9ydCA9IGMoY29ob3J0X25hbWVzX20sIGNvaG9ydF9uYW1lc19mKSwNCiAgQ291bnQgPSBjKGNvaG9ydF9jb3VudHNfbSwgY29ob3J0X2NvdW50c19mKSwNCiAgU2V4ID0gcmVwKGMoIk1hbGUiLCAiRmVtYWxlIiksIGVhY2ggPSBsZW5ndGgoY29ob3J0X25hbWVzX20pKQ0KKQ0KDQojIFBsb3R0aW5nIHBvcHVsYXRpb24gcHlyYW1pZA0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gcmVvcmRlcihDb2hvcnQsIC1Db3VudCksIHkgPSBDb3VudCwgZmlsbCA9IFNleCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInBpbmsiKSkgKyAgDQogIGNvb3JkX2ZsaXAoKSArICANCiAgbGFicyh0aXRsZSA9ICJQb3B1bGF0aW9uIFB5cmFtaWQiLA0KICAgICAgIHggPSAiUG9wdWxhdGlvbiBDb3VudCIsDQogICAgICAgeSA9ICJBZ2UgQ29ob3J0IiwNCiAgICAgICBmaWxsID0gIlNleCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICANCg0KYGBgDQoNCmBgYHtyfQ0KbmV3X2FnZXMgPC0gYygiMC00IiwNCiAgICAgICAgICAgICAgICAgIjUtOSIsDQogICAgICAgICAgICAgICAgICIxMC0xNCIsDQogICAgICAgICAgICAgICAgICIxNS0xOSIsDQogICAgICAgICAgICAgICAgICIyMC0yNCIsDQogICAgICAgICAgICAgICAgICIyNS0yOSIsIA0KICAgICAgICAgICAgICAgICAiMzAtMzQiLCANCiAgICAgICAgICAgICAgICAgIjM1LTM5IiwgDQogICAgICAgICAgICAgICAgICI0MC00NCIsIA0KICAgICAgICAgICAgICAgICAiNDUtNDkiLA0KICAgICAgICAgICAgICAgICAiNTAtNTQiLA0KICAgICAgICAgICAgICAgICAiNTUtNTkiLA0KICAgICAgICAgICAgICAgICAiNjAtNjQiLCANCiAgICAgICAgICAgICAgICAgIjY1LTY5IiwNCiAgICAgICAgICAgICAgICAgIjcwLTc0IiwgDQogICAgICAgICAgICAgICAgICI3NS03OSIsIA0KICAgICAgICAgICAgICAgICAiODAtODQiLCANCiAgICAgICAgICAgICAgICAgIjg1KyIpDQoNCmRhdGEgPC0gdGliYmxlKA0KICBBZ2UgPSBwYXN0ZTAobmV3X2FnZXMpLA0KICBNYWxlID0gc2FtcGxlKDIwMDoxMDAwLCBsZW5ndGgoY29ob3J0X25hbWVzX20pLCByZXBsYWNlID0gVFJVRSksDQogIEZlbWFsZSA9IHNhbXBsZSgyMDA6MTAwMCwgbGVuZ3RoKGNvaG9ydF9uYW1lc19mKSwgcmVwbGFjZSA9IFRSVUUpDQopDQoNCmRhdGFfbG9uZyA8LSBwaXZvdF9sb25nZXIoDQogIGRhdGEsIA0KICBjb2xzID0gYyhNYWxlLCBGZW1hbGUpLCANCiAgbmFtZXNfdG8gPSAiU2V4IiwgDQogIHZhbHVlc190byA9ICJQb3B1bGF0aW9uIg0KKQ0KDQpiYXNpY19wbG90IDwtIGdncGxvdChkYXRhX2xvbmcsIGFlcyh4ID0gQWdlLCB5ID0gaWZlbHNlKFNleCA9PSAiTWFsZSIsIC1Qb3B1bGF0aW9uLCBQb3B1bGF0aW9uKSwgZmlsbCA9IFNleCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGFicywgbGltaXRzID0gbWF4KGRhdGFfbG9uZyRQb3B1bGF0aW9uKSAqIGMoLTEsIDEpKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoeCA9ICJBZ2UiLCB5ID0gIlBvcHVsYXRpb24iLCBmaWxsID0gIlNleCIsIHRpdGxlID0gIlBvcHVsYXRpb24gUHlyYW1pZCIpDQoNCmJhc2ljX3Bsb3QNCmBgYA0KDQojIyMgQ29tcGxldGUgcGlwZWxpbmUNCg0KYGBge3J9DQpjZW5zdXNfZGF0YXNldCA8LSBvcGVuX2RhdGFzZXQoaGVyZSgiZGF0YSIsICJwcm9jZXNzZWQiLCAicGFycXVldF9kYXRhX2Nvb3JkcyIpKQ0KDQoNCnB1ZWJfbm9ybSA8LSBjZW5zdXNfZGF0YXNldCB8Pg0KICAgIGZpbHRlcihOT01fRU5UPT0iUHVlYmxhIikgfD4gDQogICAgY29sbGVjdCgpDQoNCm11bmljaXBhbGl0eSA8LSAiQWNhamV0ZSINCmxvY2FsaXR5IDwtICJTYW4gSmF2aWVyIg0KDQpwdWViX25vcm1fZmlsdCA8LSBwdWViX25vcm0gfD4gDQogICAgZmlsdGVyKE5PTV9NVU4gPT0gbXVuaWNpcGFsaXR5LCBOT01fTE9DID09IGxvY2FsaXR5KQ0KDQoNCmNvaG9ydF9uYW1lc19tIDwtIGMoIlBfMEE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF81QTlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzEwQTE0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8xNUExOV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjBBMjRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzI1QTI5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF8zMEEzNF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMzVBMzlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzQwQTQ0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF80NUE0OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTBBNTRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzU1QTU5X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF82MEE2NF9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNjVBNjlfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X00iLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9NIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfTSIsDQogICAgICAgICAgICAgICAgICAgICJQXzg1WU1BU19NIikNCg0KY29ob3J0X25hbWVzX2YgPC0gYygiUF8wQTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzVBOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMTBBMTRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzE1QTE5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8yMEEyNF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfMjVBMjlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzMwQTM0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF8zNUEzOV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNDBBNDRfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzQ1QTQ5X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF81MEE1NF9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfNTVBNTlfRiIsDQogICAgICAgICAgICAgICAgICAgICJQXzYwQTY0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF82NUE2OV9GIiwgDQogICAgICAgICAgICAgICAgICAgICJQXzcwQTc0X0YiLA0KICAgICAgICAgICAgICAgICAgICAiUF83NUE3OV9GIiwNCiAgICAgICAgICAgICAgICAgICAgIlBfODBBODRfRiIsIA0KICAgICAgICAgICAgICAgICAgICAiUF84NVlNQVNfRiIpDQoNCg0KDQpuZXdfYWdlcyA8LSBjKCIwLTQiLA0KICAgICAgICAgICAgICAgICAiNS05IiwNCiAgICAgICAgICAgICAgICAgIjEwLTE0IiwNCiAgICAgICAgICAgICAgICAgIjE1LTE5IiwNCiAgICAgICAgICAgICAgICAgIjIwLTI0IiwNCiAgICAgICAgICAgICAgICAgIjI1LTI5IiwgDQogICAgICAgICAgICAgICAgICIzMC0zNCIsIA0KICAgICAgICAgICAgICAgICAiMzUtMzkiLCANCiAgICAgICAgICAgICAgICAgIjQwLTQ0IiwgDQogICAgICAgICAgICAgICAgICI0NS00OSIsDQogICAgICAgICAgICAgICAgICI1MC01NCIsDQogICAgICAgICAgICAgICAgICI1NS01OSIsDQogICAgICAgICAgICAgICAgICI2MC02NCIsIA0KICAgICAgICAgICAgICAgICAiNjUtNjkiLA0KICAgICAgICAgICAgICAgICAiNzAtNzQiLCANCiAgICAgICAgICAgICAgICAgIjc1LTc5IiwgDQogICAgICAgICAgICAgICAgICI4MC04NCIsIA0KICAgICAgICAgICAgICAgICAiODUrIikNCg0KDQpkYXRhIDwtIHRpYmJsZSgNCiAgQWdlID0gcGFzdGUwKG5ld19hZ2VzKSwNCiAgTWFsZSA9IGFzLm51bWVyaWMocHVlYl9ub3JtX2ZpbHRbMSxjb2hvcnRfbmFtZXNfbV0pLA0KICBGZW1hbGUgPSBhcy5udW1lcmljKHB1ZWJfbm9ybV9maWx0WzEsY29ob3J0X25hbWVzX2ZdKQ0KKQ0KDQpkYXRhX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKA0KICBkYXRhLCANCiAgY29scyA9IGMoTWFsZSwgRmVtYWxlKSwgDQogIG5hbWVzX3RvID0gIlNleCIsIA0KICB2YWx1ZXNfdG8gPSAiUG9wdWxhdGlvbiINCikNCmJhc2ljX3Bsb3QgPC0gZ2dwbG90KGRhdGFfbG9uZywgYWVzKHggPSBBZ2UsIHkgPSBpZmVsc2UoU2V4ID09ICJNYWxlIiwgLVBvcHVsYXRpb24sIFBvcHVsYXRpb24pLCBmaWxsID0gU2V4KSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gYWJzLCBsaW1pdHMgPSBtYXgoZGF0YV9sb25nJFBvcHVsYXRpb24pICogYygtMSwgMSkpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiUG9wdWxhdGlvbiIsIGZpbGwgPSAiU2V4IiwgdGl0bGUgPSAiUG9wdWxhdGlvbiBQeXJhbWlkIikNCg0KYmFzaWNfcGxvdA0KYGBgDQoNCiMjIyBDYXJkDQoNCmBgYHtyfQ0KY2FyZCA8LSBjZW5zdXNfZGF0YXNldCB8Pg0KICAgIGZpbHRlcigNCiAgICAgIE5PTV9FTlQgPT0gIlB1ZWJsYSIsDQogICAgICBOT01fTVVOID09ICJBY2F0ZW5vIiwNCiAgICAgIE5PTV9MT0MgPT0gIlNhbnRhIEFuZHJlYSINCiAgICApIHw+DQogIGNvbGxlY3QoKQ0KICAgIA0KICAgIHRvdGFsX3BvcHVsYXRpb24gPC0gYXMubnVtZXJpYyhjYXJkWzEsIGMoIlBPQlRPVCIpXSwgbmEucm0gPSBUUlVFKQ0KICAgIHBhc3RlKCJUb3RhbCBQb3B1bGF0aW9uOiIsIHRvdGFsX3BvcHVsYXRpb24pDQpgYGANCg0KIyMjIFBpZQ0KDQpgYGB7cn0NCnRvdCA8LSBjZW5zdXNfZGF0YXNldCB8Pg0KICAgIGZpbHRlcihOT01fRU5UPT0iVG90YWwgbmFjaW9uYWwiKSB8PiANCiAgICBjb2xsZWN0KCkNCg0KdG90DQpgYGANCg0KYGBge3J9DQojIG9yaWdpbiA8LSBjZW5zdXNfZGF0YXNldCB8PiAgDQojICAgICAgZmlsdGVyKA0KIyAgICAgICBOT01fRU5UID09ICJQdWVibGEiLA0KIyAgICAgICBOT01fTVVOID09ICJBY2F0ZW5vIiwNCiMgICAgICAgTk9NX0xPQyA9PSAiU2FudGEgQW5kcmVhIg0KIyAgICAgKSB8Pg0KIyAgIGNvbGxlY3QoKQ0KICAgIA0KdG90DQogICAgDQogICAgIyBFeHRyYWN0IGJpcnRoIGRhdGENCiAgICBiaXJ0aF9sb2NhbCA8LSBhcy5udW1lcmljKHRvdFsxLCAiUE5BQ0VOVCJdKQ0KICAgIGJpcnRoX2Fub3RoZXIgPC0gYXMubnVtZXJpYyh0b3RbMSwgIlBOQUNPRSJdKQ0KICAgIA0KICAgICMgRGVidWdnaW5nIG91dHB1dA0KICAgIHByaW50KHBhc3RlKCJCaXJ0aCBMb2NhbDoiLCBiaXJ0aF9sb2NhbCkpDQogICAgcHJpbnQocGFzdGUoIkJpcnRoIEFub3RoZXI6IiwgYmlydGhfYW5vdGhlcikpDQogICAgDQogICAgIyBDcmVhdGUgcmF0aW8gZGF0YWZyYW1lDQogICAgcmF0aW9fZGYgPC0gdGliYmxlKA0KICAgICAgQ2F0ZWdvcnkgPSBjKCJMb2NhbCIsICJPdGhlciIpLA0KICAgICAgUmF0aW8gPSBjKGJpcnRoX2xvY2FsLCBiaXJ0aF9hbm90aGVyKQ0KICAgICkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBwZXJjZW50YWdlcw0KICAgIHJhdGlvX2RmJFBlcmNlbnRhZ2UgPC0gcmF0aW9fZGYkUmF0aW8gLyBzdW0ocmF0aW9fZGYkUmF0aW8pICogMTAwDQogICAgDQogICAgIyBQbG90IHRoZSBwaWUgY2hhcnQNCiAgICBnZyA8LSBnZ3Bsb3QocmF0aW9fZGYsIGFlcyh4ID0gIiIsIHkgPSBSYXRpbywgZmlsbCA9IENhdGVnb3J5KSkgKw0KICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICAgICAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsNCiAgICAgIHRoZW1lX3ZvaWQoKSArDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQkZDNCIsICIjRjg3NjZEIikpICsNCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocm91bmQoUGVyY2VudGFnZSksICIlIikpLCANCiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICBzaXplID0gNSwgY29sb3IgPSAid2hpdGUiLCBmb250ZmFjZSA9ICJib2xkIikNCiAgICANCiAgICBnZw0KICAgIA0KYGBgDQoNCiMjIyBSZWFkIENTViBmcm9tIFVSTA0KDQpgYGB7cn0NCnRlc3RfY3N2IDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWIudWJjLmNhL01EUy0yMDIzLTI0L0RTQ0lfNTMyX2luZGl2aWR1YWwtYXNzaWdubWVudF9tYXJjb255MS9tYXN0ZXIvZGF0YS9wcm9jZXNzZWQvZGF0YV9jb29yZHMuY3N2P3Rva2VuPUdIU0FUMEFBQUFBQUFBQUNMTzVXSzM2V0o2Uzc0RTNOVTRDWlJKSkpHQSIpDQoNCnRlc3RfY3N2DQoNCmBgYA0KDQpgYGB7cn0NCmZpbHRlcmVkX2RhdGEgPC0gZmlsdGVyKHRlc3RfY3N2LCBOT01fRU5UID09ICJQdWVibGEiKQ0KZmlsdGVyZWRfZGF0YQ0KYGBgDQoNCiMjIyBEb3dubG9hZCB3aXRoIGN1cmwNCg0KYGBge3J9DQpsaWJyYXJ5KGN1cmwpDQoNCnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHViLnViYy5jYS9NRFMtMjAyMy0yNC9EU0NJXzUzMl9pbmRpdmlkdWFsLWFzc2lnbm1lbnRfbWFyY29ueTEvbWFzdGVyL2RhdGEvcHJvY2Vzc2VkL2RhdGFfY29vcmRzLmNzdj90b2tlbj1HSFNBVDBBQUFBQUFBQUFDTE81V0szNldKNlM3NEUzTlU0Q1pSSkpKR0EiDQoNCnJlc3BvbnNlIDwtIGN1cmw6OmN1cmxfZmV0Y2hfbWVtb3J5KHVybCkNCg0KaWYgKHJlc3BvbnNlJHN0YXR1c19jb2RlID09IDIwMCkgew0KICBjc3ZfY29udGVudCA8LSByYXdUb0NoYXIocmVzcG9uc2UkY29udGVudCkNCiAgICB0ZXN0X2NzdiA8LSByZWFkLmNzdih0ZXh0ID0gY3N2X2NvbnRlbnQpDQpwcmludCh0ZXN0X2NzdikNCn0NCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoUkN1cmwpDQp4IDwtIGdldFVSTCgiaHR0cHM6Ly9yYXcuZ2l0aHViLnViYy5jYS9NRFMtMjAyMy0yNC9EU0NJXzUzMl9pbmRpdmlkdWFsLWFzc2lnbm1lbnRfbWFyY29ueTEvbWFzdGVyL2RhdGEvcHJvY2Vzc2VkL2RhdGFfY29vcmRzLmNzdj90b2tlbj1HSFNBVDBBQUFBQUFBQUFDTE81V0szNldKNlM3NEUzTlU0Q1pSSkpKR0EiKQ0KeSA8LSByZWFkLmNzdih0ZXh0ID0geCkNCnkNCmBgYA0KDQpgYGB7cn0NCiMgdGVzdF8yIDwtIGFycm93OjpyZWFkX3BhcnF1ZXQoImh0dHBzOi8vZ2l0aHViLnViYy5jYS9NRFMtMjAyMy0yNC9EU0NJXzUzMl9pbmRpdmlkdWFsLWFzc2lnbm1lbnRfbWFyY29ueTEvdHJlZS9tYXN0ZXIvZGF0YS9wcm9jZXNzZWQvcGFycXVldF9kYXRhX2Nvb3Jkcy5wYXJxdWV0IikNCmBgYA0K